# coding:utf-8
import configparser
import copy
import hashlib
import json
import time
from email.mime.text import MIMEText
from smtplib import SMTP_SSL

import requests as requests
from bs4 import BeautifulSoup


def send_emil(title, content, to):
    if config.getboolean('default', 'send.message'):
        if config.has_option(to, 'email'):
            try:
                to_add = config.get(to, 'email')
                send = json.loads(config.get('default', 'message.email.send'))
                msg = MIMEText(content, 'plain', _charset="utf-8")
                msg["Subject"] = title
                with SMTP_SSL(host=send['host'], port=465) as smtp:
                    # 登录发邮件服务器
                    smtp.login(user=send['acc'], password=send['sign'])
                    # 实际发送、接收邮件配置
                    smtp.sendmail(from_addr=send['acc'], to_addrs=to_add, msg=msg.as_string())
            except Exception as e:
                print("\n\n", "[E]邮件服务异常".center(60, "#"), '\n{}'.format(e), "\n\n")
        else:
            send_emil("未配置邮箱", "成员【{}】未配置邮箱\n\n{}".format(to, content), 'default')
            return
    else:
        print('\n', title.center(60, "^"), '\n', content)


def login(username: str, password: str, xb_login_url: str):
    time.sleep(sleep)
    print('\n', "登录".center(60, "-"))
    headers = {
        'User-Agent': http_agent
    }
    account = {"action": "user_login", "username": username, "password": password}
    print("开始登录，账号：{}，密码：{}".format(account.get("username"), account.get("password")))
    resp = requests.post(xb_login_url, account, headers=headers)
    print("登录结果: {}".format(json.loads(resp.content)))
    if resp.status_code != 200 or json.loads(resp.content)['status'] == '0':
        print("[W]登录失败: {}".format(json.loads(resp.content)['msg']))
        send_emil("账号登录失败，快来处理！", "账号{}登录失败,异常结果{}！".format(username, json.loads(resp.content)['msg']), 'default')
        return
    cookies = resp.cookies.get_dict()
    cookie = "PHPSESSID={}; wordpress_logged_in_fb59c6ff02f74b95f51bff22ce3c16a4={}; " \
             "wordpress_fb59c6ff02f74b95f51bff22ce3c16a4={}".format(
        cookies.get("PHPSESSID"),
        cookies.get("wordpress_logged_in_fb59c6ff02f74b95f51bff22ce3c16a4"),
        cookies.get("wordpress_fb59c6ff02f74b95f51bff22ce3c16a4")
    )
    return cookie


def create_article(xb_article_create_url: str, cookie: str) -> {}:
    time.sleep(sleep)
    print('\n', "创建新文章".center(60, "-"))
    headers = {
        'User-Agent': http_agent,
        'Cookie': cookie
    }
    resp = requests.get(xb_article_create_url, headers=headers)

    print("创建新文章结果: {}".format(resp.status_code))
    if resp.status_code != 200:
        print('\n', "[E]创建新文章失败".center(60, "#"), '\n异常：{}'.format(resp.text))
        send_emil("创建新文章失败，快来处理！", "异常结果{}！\n\n{}".format(resp.status_code, resp.text), 'default')
        return
    html_source = BeautifulSoup(resp.text, "html.parser")
    post_id = html_source.find("input", {"id": "post_ID"})
    if post_id is None:
        raise Exception("post_ID获取失败")
    post_id = post_id['value']
    user_id = html_source.find("input", {"id": "user-id"})
    if post_id is None:
        raise Exception("user_id获取失败")
    user_id = user_id['value']
    once = html_source.find("input", {"id": "_wpnonce"})
    if once is None:
        raise Exception("once获取失败")
    once = once['value']
    xb_seo_ = html_source.find("input", {"id": "csf_metabox_nonceseo-postmeta-box"})
    xb_seo = ""
    if xb_seo_ is not None:
        xb_seo = xb_seo_['value']

    k_seo_ = html_source.find("input", {"id": "csf_metabox_noncepost-seo_post_meta"})
    k_seo = ""
    if k_seo_ is not None:
        xb_seo = k_seo_['value']
    if xb_seo_ is None and k_seo_ is None:
        raise Exception("seo获取失败")
    return post_id, user_id, once, xb_seo, k_seo


def submit_article(xb_article_submit_url: str, cookie: str, post_id: str, user_id: str, once: str,
                   xb_seo: str, k_seo: str, article: {}):
    time.sleep(sleep)
    print('\n', "提交新文章".center(60, "-"))
    headers = {
        'User-Agent': http_agent,
        'Cookie': cookie
    }

    if len(post_id) == 0 or len(user_id) == 0 or len(once) == 0 or len(cookie) == 0:
        print("[E]创建新文章失败，信息不全。文章ID：{}，用户ID：{}，wpnonce：{}".format(post_id, user_id, once))
        raise Exception("创建新文章失败，信息不全")

    data = {
        "action": "editpost",
        "post_type": "post",
        "_wpnonce": once,
        "user_ID": user_id,
        "post_ID": post_id,
        "publish": "发布",
        "post_title": article['title'],
        "content": article['content'],
        "tax_input[post_tag]": article['tag'],
        "csf_metabox_nonceseo-postmeta-box": xb_seo,
        "csf_metabox_noncepost-seo_post_meta": k_seo,
        "seo-postmeta-box[post_titie_s]": 1,
        "seo-postmeta-box[post_titie]": article['seo_title'],
        "seo-postmeta-box[post_description_s]": 1,
        "seo-postmeta-box[description]": article['seo_desc'],
        "seo-postmeta-box[post_keywords_s]": 1,
        "seo-postmeta-box[keywords]": article['seo_key'],
        "post-seo_post_meta[_seo_title]": article['seo_title'],
        "post-seo_post_meta[_seo_desc]": article['seo_desc'],
        "post-seo_post_meta[_seo_metakey]": article['seo_key'],
    }
    resp = requests.post(xb_article_submit_url, data, headers=headers)
    print("提交新文章结果: {}".format(resp.status_code))
    if resp.status_code != 200:
        print('\n', "[E]提交新文章失败".center(60, "#"), '\n异常：{}'.format(resp.text))
        send_emil("提交新文章失败，快来处理！", "异常结果{}！\n\n{}".format(resp.status_code, resp.text), 'default')
        return


def create_submit_article(cookie, **kwargs):
    post_id, user_id, once, xb_seo, k_seo = create_article(kwargs['xb_article_create_url'], cookie)
    print("文章ID：{}，用户ID：{}，wpnonce：{}".format(post_id, user_id, once))
    submit_article(kwargs['xb_article_submit_url'], cookie, post_id, user_id, once, xb_seo, k_seo, kwargs['article'])


def get_yq_content(article_path: str, yq_base_url: str, yq_token: str):
    time.sleep(sleep)
    print('\n', "获取语雀{}文章".format(article_path).center(60, "-"))
    headers = {
        'User-Agent': http_agent,
        'X-Auth-Token': yq_token
    }
    article_url = "{}/{}".format(yq_base_url, article_path).replace("//", "/").replace(":/", "://")
    resp = requests.get(article_url, headers=headers)
    print("获取语雀文章: {}".format(resp.status_code))
    return resp


def get_yuque_article(article_path: str, yq_base_url: str, yq_token: str) -> {}:
    resp = get_yq_content(article_path, yq_base_url, yq_token)
    if resp.status_code != 200:
        print('\n', "[E]获取语雀文章失败".center(60, "#"), '\n异常：{}'.format(resp.text))
        send_emil("获取语雀文章失败，快来处理！", "异常结果{}！\n\n{}".format(resp.status_code, resp.text), 'default')
        return
    article_json = json.loads(resp.text)
    title = article_json['data']['title']
    content = article_json['data']['body']
    seo = article_json['data']['custom_description']
    article = {'title': title, 'content': content, 'tag': seo,
               'seo_title': title, 'seo_desc': seo, 'seo_key': seo}
    return article


def format_yq_article(article_content: str, return_code: str) -> str:
    return article_content.replace('\"', '"').replace('\n\n', '<br />').replace('\n', '<br />') \
        .replace('开源指北', '<strong><span style="color: #ff0000">开源指北</span></strong>') \
        .replace('${return_code}', '<strong><span style="color: #ff0000">{}</span></strong>'.format(return_code))


def send_article(send_type, user_inf, article_path_list, **kwargs):
    for art in article_path_list:
        try:
            has_opt = config.has_option(user_inf, art)
            if has_opt:
                return_code = config.get(user_inf, art)
                article = copy.deepcopy(article_cache.get(art))
                if article is None:
                    article = get_yuque_article(art, kwargs['yq_base_url'], kwargs['yq_token'])
                    article_cache[art] = copy.deepcopy(article)
                print("用户:{},发布的文章:{},关键字为:{}".format(user_inf, art, return_code))
                article['content'] = format_yq_article(article['content'], return_code)
                kwargs['article'] = article
                create_submit_article(**kwargs)
            else:
                print('\n', "[W]成员【{}】未配置{}文章回复关键字".format(user_inf, art).center(60, "*"), '\n')
        except Exception as e:
            print('\n', "[E]发布文章失败".center(60, "#"), '\n异常：{}'.format(e))
            send_emil("发布{}文章失败".format(send_type), '\n异常：{}'.format(e), 'default')


def verify_token(cookie) -> bool:
    verify_url = config.get('24k', 'verify.url')
    headers = {
        'User-Agent': http_agent,
        'Cookie': cookie
    }
    resp = requests.get(verify_url, headers=headers)
    if resp.status_code != 200 or resp.url.find('login') != -1:
        return False
    return True


def start():
    # 默认配置
    user_list = config.get('default', 'user.info.list').split(',')
    # 语雀配置
    yq_token = config.get('yuque', 'token')
    yq_base_url = config.get('yuque', 'base.url')
    article_path_list = config.get('yuque', 'article.path.list').split(",")

    # 小白学堂配置
    xb_login_url = config.get('xiaobai', 'login.url')
    xb_article_create_url = config.get('xiaobai', 'article.create.url')
    xb_article_submit_url = config.get('xiaobai', 'article.submit.url')

    # 24k配置
    k_article_create_url = config.get('24k', 'article.create.url')
    k_article_submit_url = config.get('24k', 'article.submit.url')

    for user_inf in user_list:
        print('\n', "成员【{}】开始发布".format(user_inf).center(60, "-"))
        try:
            print('\n', "发布小白学堂文章".format(user_inf).center(60, "-"))
            xb_acc_has = config.has_option('xiaobai', '{}.account'.format(user_inf))
            if xb_acc_has:
                xb_acc = json.loads(config.get('xiaobai', '{}.account'.format(user_inf)))
                xb_cookie = login(xb_acc['username'], xb_acc['password'], xb_login_url)
                if xb_cookie is not None:
                    send_article('xiaobai', user_inf, article_path_list, cookie=xb_cookie,
                                 xb_article_create_url=xb_article_create_url,
                                 xb_article_submit_url=xb_article_submit_url, yq_token=yq_token,
                                 yq_base_url=yq_base_url)
            else:
                print('\n', "[W]成员【{}】未配置小白账号".format(user_inf).center(60, "*"), '\n')
                send_emil("未配置小白账号", "成员【{}】未配置小白账号".format(user_inf), user_inf)

            print('\n', "发布24k文章".format(user_inf).center(60, "-"))
            k_user_cookie_has = config.has_option('24k', '{}.cookie'.format(user_inf))
            if k_user_cookie_has:
                k_user_cookie = config.get('24k', '{}.cookie'.format(user_inf))
                # 验证24k token是否过期
                available = verify_token(k_user_cookie)
                if available:
                    send_article('24k', user_inf, article_path_list, cookie=k_user_cookie,
                                 xb_article_create_url=k_article_create_url,
                                 xb_article_submit_url=k_article_submit_url, yq_token=yq_token, yq_base_url=yq_base_url)
                else:
                    print('\n', "[W]成员【{}】24k Cookie 过期".format(user_inf).center(60, "*"), '\n')
                    send_emil("24K TOKEN过期", "成员【{}】24K TOKEN 过期请重新配置".format(user_inf), user_inf)
            else:
                print('\n', "[W]成员【{}】未配置24k Cookie".format(user_inf).center(60, "*"), '\n')
                send_emil("未配置24k Cookie", "成员【{}】未配置24k Cookie".format(user_inf), user_inf)
            print('\n', "成员【{}】完成发布".format(user_inf).center(60, "-"), '\n')
        except Exception as e:
            print('\n', "[E]发布文章失败".center(60, "#"), '\n异常：{}'.format(e))
            send_emil("发布文章失败", '\n异常：{}'.format(e), 'default')


def flush_config():
    config.read('config.ini', encoding="utf-8")


def get_config_md5():
    config_file = open('config.ini', 'rb')
    config_content = config_file.read()
    config_file.close()
    return hashlib.md5(config_content).hexdigest()


def update_config():
    config_id = config.get('default', 'online.config.id')
    yq_token = config.get('yuque', 'token')
    yq_base_url = config.get('yuque', 'base.url')
    resp = get_yq_content(config_id, yq_base_url, yq_token)
    if resp.status_code != 200:
        print('\n', "[E]获取语雀文章失败".center(60, "#"), '\n异常：{}'.format(resp.text))
        send_emil("获取语雀文章失败，快来处理！", "异常结果{}！\n\n{}".format(resp.status_code, resp.text), 'default')
        return
    config_json = json.loads(resp.text)
    config_str = config_json['data']['body']
    config_str = config_str.replace('<br />', '\n')
    ini_file = open('config.ini', mode="w", encoding='utf-8')
    ini_file.write(config_str)


def send_interval():
    config_md5 = get_config_md5()
    if config.getboolean('default', 'open.online.config'):
        update_config()
    while 1:
        print('\n', "【时间：{}】".format(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))
              .center(60, "="), '\n')
        new_md5 = get_config_md5()
        if config.getboolean('default', 'open.online.config'):
            update_config()
        if new_md5 != config_md5:
            print('\n', "配置文件更新".center(60, "-"), '\n')
            flush_config()
            article_cache.clear()
            config_md5 = new_md5
        seconds = int(config.get('default', 'send.interval.seconds'))
        print('\n', "每[{}]秒执行一次".format(seconds).center(60, "-"), '\n')
        start()
        print('\n', "等待下次调度".center(60, "="))
        time.sleep(seconds)


if __name__ == '__main__':
    config = configparser.RawConfigParser()
    config.read('config.ini', encoding="utf-8")
    sleep = int(config.get('default', 'http.sleep'))
    http_agent = config.get('default', 'http.agent')
    article_cache = {}
    send_interval()
